/*********************************************************************************
 *      Copyright:  (C) 2023 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  gpiod_led_test.c
 *    Description:  This file  used to test tricolour light blink.
 *                 
 *        Version:  1.0.0(2023年05月19日)
 *         Author:  Ling Yun <lingyun@email.com>
 *      ChangeLog:  1, Release initial version on "2023年05月19日 16时53分23秒"
 *                 
 ********************************************************************************/

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <gpiod.h> //gpio头文件支持，链接时加上 -lgpio

#define OFF	0
#define ON	1

typedef struct gpiod_led_s
{
	struct	gpiod_chip *chip;//gpio芯片
	struct	gpiod_line *line;//gpio控制口
}gpiod_led_t;

enum LED_INDEX
{
	LED_RED = 0,
	LED_GREEN,
	LED_BLUE,
	LED_MAX,
};

int led_init(gpiod_led_t *gpiod_led,unsigned char gpio_chip_num, unsigned char gpio_off_num);
int led_control(gpiod_led_t *gpiod_led, int status);
static inline void msleep(unsigned long ms);
void blink_led(gpiod_led_t *gpiod_led, unsigned int interval);
int led_release(gpiod_led_t *gpiod_led);

int main(int argc, char *argv[])
{
	gpiod_led_t led[LED_MAX];

	if(led_init(&led[LED_BLUE],5,9) < 0)
	{
		printf("LED_BLUE init error.\n");
		return -1;
	}
	
	if(led_init(&led[LED_RED],1,11) < 0)
	{
		printf("LED_RED init  error.\n");
		return -1;
	}
	
	if(led_init(&led[LED_GREEN],1,10) < 0)
	{
		printf("LED_GREEN init error.\n");
		return -1;
	}

	while(1)
	{
		blink_led(&led[LED_BLUE], 500);
		blink_led(&led[LED_RED], 500);
		blink_led(&led[LED_GREEN], 500);
	}
	led_release(&led[LED_BLUE]);
	led_release(&led[LED_RED]);
	led_release(&led[LED_GREEN]);

	return 0;
}

/* led初始化函数 */
int led_init(gpiod_led_t *gpiod_led, unsigned char gpio_chip_num, unsigned char gpio_off_num)
{
	char dev_name[16];

	if(gpio_chip_num == 0 || gpio_chip_num > 5 || gpio_off_num == 0 || gpio_off_num > 32 || !gpiod_led )
	{
		printf("[INFO] %s argument error.\n",__FUNCTION__);
		return -1;
	}

	memset(dev_name, 0, sizeof(dev_name));
	snprintf(dev_name, sizeof(dev_name), "/dev/gpiochip%d", gpio_chip_num-1);

	if(!(gpiod_led->chip = gpiod_chip_open(dev_name)))
	{
		printf("fail to open chip0\n");
		return -2;
	}

	if(!(gpiod_led->line = gpiod_chip_get_line(gpiod_led->chip, gpio_off_num)))
	{
		printf("fail to get line_led\n");
		return -3;
	}

	/* 设置初始值为灭 */
	if(gpiod_line_request_output(gpiod_led->line, "led_out", OFF) < 0)
	{
		printf("fail to request line_led for output mode\n");
		return -4;
	}

	return 0;
}

/* ms级休眠函数 */
static inline void msleep(unsigned long ms)
{
	struct timespec cSleep;
	unsigned long ulTmp;

	cSleep.tv_sec = ms / 1000;
	if(cSleep.tv_sec == 0)
	{
		ulTmp = ms * 10000;
		cSleep.tv_nsec = ulTmp * 100;
	}
	else
	{
		cSleep.tv_nsec = 0;
	}
	
	nanosleep(&cSleep, 0);
}

/* led控制函数 */
int led_control(gpiod_led_t *gpiod_led, int status)
{
	int level;
	if( !gpiod_led )
	{
		printf("[INFO] %s argument error.\n", __FUNCTION__);
		return -1;
	}

	level = (status==ON) ? ON: OFF; //纠正status参数
	if(gpiod_line_set_value(gpiod_led->line, level) < 0)
	{
		printf("fail to set line_led value\n");
		return -2;
	}

	return 0;
}

/* led闪烁函数 */
void blink_led(gpiod_led_t *gpiod_led, unsigned int interval)
{
	led_control(gpiod_led, ON);
	msleep(interval);

	led_control(gpiod_led, OFF);
	msleep(interval);    
}

/* led gpiod释放和关闭函数 */
int led_release(gpiod_led_t *gpiod_led)
{
	if(!gpiod_led)
	{
		printf("[INFO] %s argument error.\n",__FUNCTION__);
		return -1;
	}
	gpiod_line_release(gpiod_led->line);
	gpiod_chip_close(gpiod_led->chip);

	return 0;
}

